package com.idea.relax.log.appender.logstash;

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.LoggerContext;
import com.idea.relax.log.appender.ILogbackAppender;
import com.idea.relax.log.constant.LogConstant;
import com.idea.relax.log.props.LogLevel;
import com.idea.relax.log.props.RelaxLogProperties;
import com.idea.relax.log.support.utils.*;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.appender.LogstashTcpSocketAppender;
import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders;
import net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder;
import net.logstash.logback.encoder.LogstashEncoder;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;

import java.util.Set;

/**
 * @author L.cm
 * @author salad(参考自L.cm的Mica)
 * @description: Logstash的appender
 * @date: 2022/12/17
 */
@Slf4j
public class LoggingLogStashAppender implements ILogbackAppender {

    private final RelaxLogProperties properties;

    public LoggingLogStashAppender(RelaxLogProperties properties) {
        this.properties = properties;
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        this.start(context);
    }

    @Override
    public void start(LoggerContext context) {
        log.info("LogStash logging start.");
        apply(context);
    }

    @Override
    public void reset(LoggerContext context) {
        log.info("LogStash logging reset.");
        apply(context);
    }

    @Override
    public void apply(LoggerContext context) {
        RelaxLogProperties.LogstashLog props = properties.getLogstashLog();
        if (props.isEnabled()) {
            Set<LogLevel> logLevels = props.getLogLevels();
            if (Func.isEmpty(logLevels)) {
                addLevelLogStashTcpSocketAppender(context, LogLevel.ALL, props);
            } else {
                for (LogLevel level : logLevels) {
                    addLevelLogStashTcpSocketAppender(context, level, props);
                }
            }
            log.info("LogStash logging apply successfully.");
        }
    }

    /**
     * addLevelLogStashTcpSocketAppender.
     */
    private void addLevelLogStashTcpSocketAppender(LoggerContext context, LogLevel level,
                                                   RelaxLogProperties.LogstashLog props) {
        // More documentation is available at: https://github.com/logstash/logstash-logback-encoder
        final LogstashTcpSocketAppender logStashAppender = new LogstashTcpSocketAppender();
        String name = props.getAppenderNamePrefix().concat(level.getLevel().levelStr);
        logStashAppender.addDestination(props.getDestinations());
        logStashAppender.setContext(context);
        logStashAppender.setName(name);
        logStashAppender.setEncoder(logstashEncoder(context));
        logStashAppender.setRingBufferSize(props.getRingBufferSize());
        logStashAppender.addFilter(LoggingUtil.levelFilter(context, level));

        if (props.isAsyncAppender()) {
            AsyncAppender asyncAppender = LoggingUtil.bindAsyncAppender(logStashAppender, 512, 0, false);
            asyncAppender.start();
        } else {
            logStashAppender.start();
        }
        decideApplyAppender(context, name, logStashAppender);
    }


    private LoggingEventCompositeJsonEncoder logstashEncoder(LoggerContext context) {
        LoggingEventCompositeJsonEncoder logstashEncoder = new LoggingEventCompositeJsonEncoder();
//        logstashEncoder.setThrowableConverter(LogStashUtil.throwableConverter());
        LoggingEventJsonProviders loggingEventJsonProviders = LogStashUtil.jsonProviders(context);
        loggingEventJsonProviders.addStackHash(LogStashUtil.stackHashJsonProvider("stackHash", LogConstant.EXCLUSION_PATTERNS));
        logstashEncoder.setProviders(loggingEventJsonProviders);
        return logstashEncoder;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

}
